#include <buola/gui/x11/auto.h>

#include <buola/app/ceventloop.h>
#include <buola/os/cuser.h>
#include <buola/gui/cwindow.h>
#include <buola/gui/x11/cx11session.h>
#include <buola/os/cenv.h>

#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"

namespace buola { namespace gui { namespace x11 {

CX11Session::CX11Session(CToolkit *pToolkit)
{
    mToolkit=pToolkit;
}

CX11Session::~CX11Session()
{
}

void CX11Session::Start()
{
    mSMConn=nullptr;
    mSMListener=nullptr;

    SMReset();

    std::string lSM=CEnv::Get("SESSION_MANAGER");
    if(!lSM.length())
    {
        msg_warn() << "No session manager running!!\n";
        return;
    }

    SmcCallbacks lCB;

    lCB.save_yourself.callback=SMSaveYourselfCallback;
    lCB.save_yourself.client_data=(SmPointer)this;
    lCB.die.callback=SMDieCallback;
    lCB.die.client_data=(SmPointer)this;
    lCB.save_complete.callback=SMSaveCompleteCallback;
    lCB.save_complete.client_data=(SmPointer)this;
    lCB.shutdown_cancelled.callback=SMShutdownCancelledCallback;
    lCB.shutdown_cancelled.client_data=(SmPointer)this;

    char *lNewID=nullptr;
    char lError[256];

    lError[0]=0;

    mSMConn=SmcOpenConnection(nullptr,nullptr,1,0,
            SmcSaveYourselfProcMask|SmcDieProcMask|
            SmcSaveCompleteProcMask|SmcShutdownCancelledProcMask,&lCB,
            mSMID.length()?const_cast<char*>(mSMID.c_str()):((char*)nullptr),
            &lNewID,256,lError);

    if(lNewID)
        mSMID=lNewID;
    else
        mSMID.clear();

    free(lNewID);

    if(!mSMConn)
    {
        msg_error() << "Can't connect to session manager: " << lError << "\n";
        return;
    }

    mSMListener=new io::CFDStream(IceConnectionNumber(SmcGetIceConnection(mSMConn)),false);
    mSMListener->SetActivityDelegate(this,io::ENotify::READ);
}

void CX11Session::Finish()
{
    if(mSMConn)
    {
        SmcCloseConnection(mSMConn,0,nullptr);
        mSMConn=nullptr;
    }
}

/////////////////////////////////////////////////////////
///////////////////// internal //////////////////////////
/////////////////////////////////////////////////////////

void CX11Session::SMReset()
{
    mSMWaitingInteraction=false;
    mSMInteracting=false;
    mSMActive=false;
    mSMWantPhase2=false;
    mSMInPhase2=false;
    mSMInShutdown=false;
    mSMNoUserInput=false;
    mSMInteractStyle=SmInteractStyleNone;
}

void CX11Session::OnStreamCanRead(io::CFDStream*)
{
    IceProcessMessages(SmcGetIceConnection(mSMConn),0,0);
}

void CX11Session::SMSetProperty(const char *pName,const std::string &pValue)
{
    SmPropValue lPropValue;
    int lLength=pValue.length();
    char lValue[lLength+1];
    for(int i=0;i<=lLength;i++)
        lValue[i]=pValue[i];

    lPropValue.length=lLength;
    lPropValue.value=(SmPointer)lValue;

    SmProp lProp;
    lProp.name=const_cast<char*>(pName);
    lProp.type=const_cast<char*>(SmARRAY8);
    lProp.num_vals=1;
    lProp.vals=&lPropValue;

    SmProp *lProps[1];
    lProps[0]=&lProp;

    SmcSetProperties(mSMConn,1,lProps);
}

void CX11Session::SMSetListProperty(const char *pName,const std::vector<std::string> &pValue)
{
    SmPropValue lPropValue[pValue.size()];

    char *lValue[pValue.size()];

    for(int i=0;i<pValue.size();i++)
    {
        int lLength=pValue[i].length();
        lValue[i]=new char[lLength+1];
        for(int j=0;j<=lLength;j++)
            lValue[i][j]=pValue[i][j];

        lPropValue[i].length=lLength;
        lPropValue[i].value=(SmPointer)lValue[i];
    }

    SmProp lProp;
    lProp.name=const_cast<char*>(pName);
    lProp.type=const_cast<char*>(SmLISTofARRAY8);
    lProp.num_vals=pValue.size();
    lProp.vals=lPropValue;

    SmProp *lProps[1];
    lProps[0]=&lProp;

    SmcSetProperties(mSMConn,1,lProps);

    for(int i=0;i<pValue.size();i++)
    {
        delete[] lValue[i];
    }
}

void CX11Session::SMSaveYourself(int pType,bool pShutdown,int pInteractStyle,
                                    bool pFast)
{
    mSMInteractStyle=pInteractStyle;
    mSMInShutdown=pShutdown;
    mSMActive=true;
    mSMSaveType=pType;
    mSMFast=pFast;

    ///\todo
//    SMSetProperty(SmProgram,get_base_program_name());
//
//    CUserInfo *lUser=CUserInfo::GetEffUserInfo();
//    lUser->GetGroupCount();
//    SMSetProperty(SmUserID,utf8(lUser->GetName()));
//
//    mSMRestart.clear();
//    mSMRestart.push_back(get_base_program_name());
//    mSMRestart.push_back("-session");
//    mSMRestart.push_back(mSMID);
//
//    SMSetListProperty(SmRestartCommand,mSMRestart);

    SmcSaveYourselfDone(mSMConn,true);

    SMReset();
}

void CX11Session::SMSaveYourselfCallback(SmcConn,SmPointer pSession,int pType,
                        Bool pShutdown,int pInteractStyle,Bool pFast)
{
    ((CX11Session*)pSession)->SMSaveYourself(pType,pShutdown,
                                        pInteractStyle,pFast);
}

void CX11Session::SMDieCallback(SmcConn,SmPointer /*pSession*/)
{
    close_all_main_windows();
}

void CX11Session::SMShutdownCancelledCallback(SmcConn,SmPointer)
{
}

void CX11Session::SMSaveCompleteCallback(SmcConn,SmPointer)
{
}

void CX11Session::SMInteractCallback(SmcConn,SmPointer)
{
}

void CX11Session::SMSaveYourselfPhase2Callback(SmcConn,SmPointer)
{
}

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }
